package collection;

import java.util.Iterator;

public abstract class ABinTree<N extends ABinTreeNode<E>, E> extends ATree<N, E> {

	public ABinTree(){}
	
	@Override
	public boolean isInternal(N someNode) 
	{
		return (getLeftChild(someNode)!=null || getRightChild(someNode) != null);
	}
	
	@Override
	public boolean isExternal(N someNode) 
	{
		return !isInternal(someNode);
	}
	
	public abstract boolean hasLeftChild(N someNode);
	
	public abstract boolean hasRightChild(N someNode);
	
	public abstract N getLeftChild(N someNode);
	
	public abstract N getRightChild(N someNode);
	
	public abstract void addLeftChildElement(N someNode, E element);
	
	public abstract void addRightChildElement(N someNode, E element);
	
	public abstract void removeNode(N someNode);
	
	@Override
	public abstract N getParent(N someNode);

	@Override
	public Iterable<N> getChildren(N someNode) 
	{
		final SLinkedList<N> childList = new SLinkedList<N>();
		if (getLeftChild(someNode) != null)
			childList.addLast(childList.createNode(getLeftChild(someNode)));
		if (getRightChild(someNode) != null)
			childList.addLast(childList.createNode(getRightChild(someNode)));

		return new Iterable<N>(){

			@Override
			public Iterator<N> iterator() 
			{
				return childList.elementsIterator();
			}
			
		};
	}
	
	@Override
	public void preOrderTraverse(N someNode, ANodeCommand<E> command)
	{
		command.receiveNode(someNode).execute();
		
		if (hasLeftChild(someNode))
		{
			preOrderTraverse(getLeftChild(someNode), command);
		}
		
		if (hasRightChild(someNode))
		{
			preOrderTraverse(getRightChild(someNode), command);
		}
	}
	
	@Override
	public void postOrderTraverse(N someNode, ANodeCommand<E> command)
	{
		if (hasLeftChild(someNode))
		{
			preOrderTraverse(getLeftChild(someNode), command);
		}
		
		if (hasRightChild(someNode))
		{
			preOrderTraverse(getRightChild(someNode), command);
		}
		
		command.receiveNode(someNode).execute();
	}
	
	public void inOrderTraverse(N someNode, ANodeCommand<E> command)
	{
		if (hasLeftChild(someNode))
		{
			inOrderTraverse(getLeftChild(someNode), command);
		}
		
		command.receiveNode(someNode).execute();
		
		if (hasRightChild(someNode))
		{
			inOrderTraverse(getRightChild(someNode), command);
		}
	}

}
